#ifndef BONDERHEAD_H
#define BONDERHEAD_H

#include "AxisModule/singleaxis.h"
#include "AxisModule/xyzmodule.h"
#include "CompositeElement/sccylinder.h"
#include "CompositeElement/scvacuum.h"
#include "CompositeElement/twoaxisplanner.h"
#include "Tray/trayconfig.h"
#include "TwoDimCali/twodimensioncalibration.h"
#include "VisionManager/visionmanager.h"
#include "bonderheadconfig.h"
#include "errorHandling/scrunnable.h"
#include "namedefinition.h"
#include "systemCore/worker.h"
#include "uphhelper.h"
#include "workers/BonderWorkHolder/bonderworkholder.h"
#include "workers/LensTrayModule/lenstrayloader.h"
#include <QThreadPool>

SILICOOL_DECLARE_LOGGING_CATEGORY(bondingTimeLog, )

const QString AutoPlaceBack_ = "AutoPlaceBack";

class LensUplookPrError : public SilicolError
{
public:
    LensUplookPrError()
        : SilicolError(WorkerName::BonderHead,
                       QObject::tr("Lens uplook pr failed!\r\nOption explanation:\r\nRetry: Redo uplook pr. "
                                   "ManuallyTakeAway: Manually place the lens to lens tray, then bond head will pick "
                                   "next ok lens. "))
    {
        appendErrorHandlingOption(Retry_);
        appendErrorHandlingOption(ManuallyTakeAway_);
        appendErrorHandlingOption(Abort_);
    }

    // SilicolError interface
protected:
    virtual void implemented() override {}
};

class BonderHead;

class SoftLandingPickDownCmd : public SCRunnable
{
public:
    SoftLandingPickDownCmd(BonderHead *bh) : bh(bh) {}

    // SCRunnable interface
public:
    virtual void runImpl() override;

private:
    BonderHead *bh;
};

class PerformUplookPrAndCorrectTargetPosCmd : public SCRunnable
{
public:
    PerformUplookPrAndCorrectTargetPosCmd(BonderHead *bh) : bh(bh) {}

    void resetCmdResult();

    bool cmdResult() const
    {
        return m_cmdResult;
    }

    // SCRunnable interface
public:
    virtual void runImpl() override;

private:
    BonderHead *bh;
    bool m_cmdResult = false;
};

class BonderHead : public Worker
{
    Q_OBJECT

    friend class SoftLandingPickDownCmd;
    friend class PerformUplookPrAndCorrectTargetPosCmd;

public:
    BonderHead(TrayConfig *sensorTrayConfig,
               TrayConfig *lensTrayConfig,
               BonderWorkHolder *bwh,
               LensTrayLoader *ltLoader,
               UPHHelper *uphHelper);

    ~BonderHead() override;

    BonderHeadConfig *getBhConfig() const
    {
        return bhConfig;
    }

    // Worker interface
protected:
    virtual void initEventHandle() override;
    virtual void startEventHandle(const WorkerEvent &event) override;
    virtual void stopEventHandle() override {}
    virtual void allWorkerStopped() override;
    virtual void purgeEventHandle() override;
    virtual void resetEventHandle() override;
    virtual void homeWorker(bool moveToReadyPosAfterHome) override;

    // 自动运行逻辑
public slots:
    void startRun();
    void waitLens();
    void directMoveToPickLensPos();
    void pickLens();
    void moveToUplookPos();
    void waitSensor();
    void performUplookPrAndMoveToBondPos();
    void bond();
    void postBond();
    void bwhStopped();

    // 手动调试操作
public slots:
    void readLTCaliGlassPrOffset();
    void estimateBHToLTCameraOffset();
    void ltCaliGlassStaticPrDeviationTest(int times);
    void ltCaliGlassStaticPickPlaceDeviationTest(int times, int delayAfterMoveToPickPos);
    void calibrateBHToLTCameraOffset();
    void moveToPickLTCaliGlass();
    void moveToCalibrateCor();
    void calibrateLTCameraToUplookCameraOffset();
    void doLensTraySideTwoDimensionCalibration();
    void placeCaliGlassToLTCameraCenter();

    void readSTCaliGlassPrOffset();
    void estimateBHToSTCameraOffset();
    void stCaliGlassStaticPrDeviationTest(int times);
    void stCaliGlassStaticPickPlaceDeviationTest(int times, int delayAfterMoveToPickPos);
    void stCaliGlassPickPlaceDeviationTest(int times);
    void calibrateBHToSTCameraOffset();
    void moveToPickSTCaliGlass();
    void calibrateSTCameraToUplookCameraOffset();
    void placeCaliGlassToSTCameraCenter();
    void resetSTCameraToUplookCameraOffsetOfEachRow();
    void calibrateSTCameraToUplookCameraOffsetOfEachRow();
    void doSensorTraySideTwoDimensionCalibration(int rowIndex, bool calibrateAllRows);
    void testBondOfEachRow(double maxRandomXYOffsetForPick_mm,
                           double maxRandomRotationAfterPick_deg,
                           bool useCorrespondingCalibrationForEachRow,
                           double targetMaxRandomXOffset_pixel,
                           double targetMaxRandomYOffset_pixel,
                           double targetMaxRandomThetaOffset_deg,
                           int testTimes);
    void stopTestBondOfEachRow();

    void manuallyPickLens(int unitRowIndex, int unitColumnIndex, int rowIndex, int columnIndex);
    void moveToPerformUplookPr();
    void manuallyPlaceBackLens(int unitRowIndex, int unitColumnIndex, int rowIndex, int columnIndex);
    void manuallyBond(int unitRowIndex, int unitColumnIndex, int rowIndex, int columnIndex, bool doPostBond);

    void doPick();
    void doPlace();

    void readSafetyPosNearLt();
    void readSafetyPosNearSt();
    void readLtSafetyHeightAfterPickLens();
    void readStSafetyHeightAfterPlaceLens();

    void resetBHYZAcc();

private:
    void
    ltCaliGlassCustomCaliFunc(double xMove, double yMove, QVector<QPointF> &pixelPoints, QVector<QPointF> &motorPoints);
    void ltCaliGlassPerformPrResultFunc(PrOffset prOffset);
    void
    stCaliGlassCustomCaliFunc(double xMove, double yMove, QVector<QPointF> &pixelPoints, QVector<QPointF> &motorPoints);
    void stCaliGlassPerformPrResultFunc(PrOffset prOffset);
    void caliGlassCustomCaliFunc(Calibration *calibration,
                                 QString pickCaliGlassPos,
                                 double xMove,
                                 double yMove,
                                 QVector<QPointF> &pixelPoints,
                                 QVector<QPointF> &motorPoints,
                                 bool isLt);

    void moveToStandbyPos(bool moveBhZFirst);
    void moveTwoZAxisToStandbyPos();
    void pickDut(bool upBondHeadZ = false);
    void placeDut(bool upBondHeadZ = false);
    void pickDutDown();
    void pickDutDownAction();
    void softlandUpMiniZWithoutWait();
    void checkDutAfterPick();
    void placeDutDown();
    void placeDutDownAction();
    void softLandUpTwoZ();
    ///
    /// \brief moveToCalibrateCOR Get nozzel center to calibration glass center offset
    /// \return
    ///
    QPointF getCorToUplookCameraOffset();
    PrOffset getLTCaliGlassPrOffset();
    PrOffset getCaliGlassUplookPrOffset();
    PrOffset getSTCaliGlassPrOffset();
    PrOffset getLensUplookOffset();
    PrOffset getLensUplookRectOffset();
    bool getLensUplookOffsetImpl(PrOffset &prOffset);
    void showDeviationTestResult(QString tipTitle,
                                 QString reportTitle,
                                 QList<double> &xOffsets,
                                 QList<double> &yOffsets,
                                 QList<double> &thetaOffsets);
    QString getMaxMinAveInfo(QString title, QList<double> &values);
    void moveToPlaceCaliGlassToLTCameraPos(PrOffset &uplookOffset,
                                           bool useCorFromCalibration = true,
                                           double targetXOffset = 0,
                                           double targetYOffset = 0,
                                           double targetThetaOffset = 0);
    void moveToPlaceCaliGlassToSTCameraPos(PrOffset &uplookOffset,
                                           CameraOffset *cameraToUplookCameraOffset,
                                           bool useCorFromCalibration = true,
                                           double targetXOffset = 0,
                                           double targetYOffset = 0,
                                           double targetThetaOffset = 0,
                                           TwoDimensionCalibration *tdc = nullptr);
    void fastMoveToStCameraPlacePos(PrOffset &uplookOffset,
                                    CameraOffset *cameraToUplookCameraOffset,
                                    bool rotate,
                                    double targetXOffset,
                                    double targetYOffset,
                                    double targetThetaOffset,
                                    TwoDimensionCalibration *tdc = nullptr);
    void moveToPlaceDutToCameraPos(PrOffset &uplookOffset,
                                   CameraOffset *cameraToUplookCameraOffset,
                                   double readyForPlaceZHeight,
                                   bool rotate,
                                   double targetXOffset,
                                   double targetYOffset,
                                   double targetThetaOffset,
                                   TwoDimensionCalibration *tdc = nullptr);
    void moveToPickSTCaliGlassPos(double targetXOffset = 0, double targetYOffset = 0);
    double getRand(double min, double max);

    void verifyTdcParam(TwoDimensionCaliParameter *tdcParam);
    void doTwoDimensionCalibration(bool isSensorTraySide, int sensorTraySideRowIndex = 0);
    QString stSideTdcFileName(int rowIndex) const;
    QString ltSideTdcFileName() const;
    void freeTdcDatas();
    void loadTdcDatas();
    TwoDimensionCalibration *stSideTdc(int rowIndex);

    void checkLensIndex();
    void checkSensorIndex();
    void pushPickLensEvent();
    void pushGetLensEventToLTL();
    void pushGetSensorEventToBWH();
    void handleUplookPrFailed();
    void handleStop();

    void directMoveToPickLensPosImpl(PrOffset &lensPrOffset);
    void directMoveToPlacebackLensPos(PrOffset &lensPrOffset);
    void moveFromLtToUplookPosImpl();
    void moveFromStToStandbyPosImpl();

    void setHigherBHYZAcc();

private:
    TrayConfig *sensorTrayConfig;
    TrayConfig *lensTrayConfig;
    BonderWorkHolder *bwh;
    LensTrayLoader *ltLoader;

    QThreadPool *thdPool;
    SoftLandingPickDownCmd *softLandingPickDownCmd;
    PerformUplookPrAndCorrectTargetPosCmd *performUplookPrAndCorrectTargetPosCmd;

    UPHHelper *uphHelper;

    TwoAxisPlanner *yzPlanner;
    TwoAxisPlanner *zyPlanner;

    QList<TwoDimensionCalibration *> stSideTdcs;
    TwoDimensionCalibration *ltSideTdc = nullptr;

    SingleAxis *bhZ;
    XYZModule *bhXyz;
    SingleAxis *bhTheta;
    SingleAxis *bhMiniZ;
    SCAxis *bhMiniZMotor;
    SCAxis *bhThetaMotor;
    SCAxis *bwhOpticYMotor;
    SCAxis *ltLoaderTrackX;
    SCVacuum *bhVacuum;

    XYModule *bwhOpticXY;
    SingleAxis *bwhOpticZ;

    BonderHeadConfig *bhConfig;
    PickPlaceConfig *pickPlaceConfig;
    ConfigFile *bhConfigFile;

    VisionLocation *ltCaliGlassLocation;
    VisionLocation *stCaliGlassLocation;
    VisionLocation *caliGlassUplookLocation;
    VisionLocation *lensUplookRectLocation;
    VisionLocation *lensUplookCircleLocation;

    QPoint pickedLensIndex;
    bool isBwhStopped = false;

    bool isTestBondOfEachRow = false;
};

#endif    // BONDERHEAD_H
